package com.frame.library.db;

import android.content.ContentValues;
import android.database.sqlite.SQLiteDatabase;
import android.util.ArrayMap;
import android.util.Log;

import com.frame.library.db.curd.QuerySupport;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;

/**
 * Added by Tom on 2024/06/28.
 * "create table if not exists Person(id integer primary key autoincrement, name text, age integer, flag boolean)"
 * create table if not exists Person (id integer primary key autoincrement,age integer, name text)
 */
public class DaoSupport<T> implements IDaoSupport<T> {
    private static final String TAG = "DaoSupport";
    private SQLiteDatabase mSqliteDatabase; // 数据库db
    private Class<T> mClazz; // 泛型类
    // 优化：参数 key-value，参考的Google的系统view创建 AppCompatViewInflater.java
    private static final Object[] mPutMethodArgs = new Object[2];
    // 缓存方法(缓存所有的put方法)
    private static final Map<String, Method> mPutMethods = new ArrayMap<>();

    public void init(SQLiteDatabase database, Class<T> clazz) {
        this.mSqliteDatabase = database;
        this.mClazz = clazz;

        // 创建表sql, 通过反射获取Person字段信息, 然后创建表。
        StringBuffer sb = new StringBuffer();
        // 反射拿到TableName，表如果不存在就创建表。
        sb.append("create table if not exists ")
                .append(DaoUtil.getTableName(clazz)) // 获取class的名称: Person
                .append(" (id integer primary key autoincrement, ");

        Field[] fields = clazz.getDeclaredFields(); // 反射: 拿到类的所有属性名字和类型
        for (Field field : fields) { // 遍历所有属性
            field.setAccessible(true);
            String name = field.getName(); // 属性名
            String type = field.getType().getSimpleName(); // 属性Type: int,String,boolean
            // type 需要进行转换，int转成integer，String转成text，
            sb.append(name).append(DaoUtil.getColumnType(type)).append(", ");
        }
        // 最后的", "  替换成")"
        sb.replace(sb.length() - 2, sb.length(), ")");
        String sql = sb.toString(); // 待执行的sql语句。createTableSql
        Log.d(TAG, "create table sql: " + sql);
        mSqliteDatabase.execSQL(sql); // 执行创建表
    }

    @Override
    public long insert(T obj) {
        // 使用的还是原生的方式，只不过我们封装了而以。
        // 一般的三方数据库，都是通过反射的形式实现的。
        /**
         * ContentValues values = new ContentValues();
         * values.put("name", person.getName());
         * db.insert("Person", null, values);
         */
        ContentValues values = contentValuesByObj(obj);
        // null 代表插入所有。
        return mSqliteDatabase.insert(DaoUtil.getTableName(mClazz), null, values);
    }


    //region 查询的支持部分
    private QuerySupport<T> mQuerySupport = null;
    @Override
    public QuerySupport<T> querySupport() {
        if (mQuerySupport == null) {
            mQuerySupport = new QuerySupport<>(mSqliteDatabase, mClazz);
        }
        return mQuerySupport;
    }
    // 删除，更新：要对最原始的写法，比较明了。
    public int delete(String whereClause, String[] whereArgs) {
        return mSqliteDatabase.delete(DaoUtil.getTableName(mClazz),
                whereClause, whereArgs);
    }
    public int update(T obj, String whereClause, String... whereArgs) {
        ContentValues values = transformContentValue(obj);
        return mSqliteDatabase.update(DaoUtil.getTableName(mClazz),
                values, whereClause, whereArgs);
    }
    //endregion

    // obj 对象转为ContentValues
    private ContentValues transformContentValue(T obj) {
        ContentValues values = new ContentValues();
        Field[] fields = mClazz.getDeclaredFields();
        for (Field field : fields) {
            try {
                field.setAccessible(true);
                Object value = field.get(obj);
                String name = field.getName();
                if (value == null) continue;
                Method valueMethod = ContentValues.class.getMethod("put",
                        String.class, value.getClass());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return values;
    }


    @Override
    public void insert(List<T> datas) { // 批量插入
        mSqliteDatabase.beginTransaction(); // 1.使用事物优化
        for (T data : datas) {
            long no = insert(data); // 调用单条插入，实现批量插入。
            Log.e(TAG, "insert: no:" + no);
        }
        mSqliteDatabase.setTransactionSuccessful();
        mSqliteDatabase.endTransaction();
    }

//    to delete，查询移动到QuerySupport中。
//    @Override
//    public List<T> query() { // 查询所有
//        Cursor cursor = mSqliteDatabase.query(DaoUtil.getTableName(mClazz),
//                null, null, null,
//                null, null, null);
//        return cursorToList(cursor);
//    }
//
//    // 将游标转对象列表
//    private List<T> cursorToList(Cursor cursor) {
//        List<T> list = new ArrayList<>();
//        if (cursor != null && cursor.moveToFirst()) {
//            do { // 不断从游标里面获取数据
//                try {
//                    // ①.反射构建新的对象，Person类必须要带有空参的构造方法。
//                    T instance = mClazz.newInstance();
//                    Field[] fields = mClazz.getDeclaredFields(); // ②.反射拿到所有属性
//                    for (Field field : fields) { // 遍历属性
//                        field.setAccessible(true);
//                        String name = field.getName();
//                        int index = cursor.getColumnIndex(name); // 获取角标，在第几列
//                        if (index == -1) continue;
//
//                        // ③.通过反射获取游标的方法，获取属性的类型 的方法
//                        Method cursorMethod = cursorMethod(field.getType());
//                        if (cursorMethod != null) {
//                            // ④.通过反射，从数据库 获取了value
//                            Object value = cursorMethod.invoke(cursor, index);
//                            if (value == null) continue;
//
//                            // 处理一些特殊的部分（Boolean，char，Date）
//                            if (field.getType() == boolean.class || field.getType() == Boolean.class) {
//                                if ("0".equals(String.valueOf(value))) {
//                                    value = false;
//                                } else if ("1".equals(String.valueOf(value))) {
//                                    value = true;
//                                }
//                            } else if (field.getType() == char.class || field.getType() == Character.class) {
//                                value = ((String) value).charAt(0);
//                            } else if (field.getType() == Date.class) {
//                                long date = (Long) value;
//                                if (date <= 0) {
//                                    value = null;
//                                } else {
//                                    value = new Date(date);
//                                }
//                            }
//                            // ⑤.通过反射注入到field相应的值。
//                            field.set(instance, value);
//                        }
//                    }
//                    // ⑥.加入item到集合中
//                    list.add(instance);
//                } catch (Exception e) {
//                    e.printStackTrace();
//                }
//            } while (cursor.moveToNext()); // 不断从里面获取数据
//        }
//        cursor.close();
//        return list; // ⑦.返回list集合
//    }
//    // 获取游标的方法
//    private Method cursorMethod(Class<?> type) throws Exception {
//        // String-> getString; int-> getInt; boolean-> getBoolean
//        String methodName = getColumnMethodName(type);
//        Method method = Cursor.class.getMethod(methodName, int.class);
//        return method;
//    }
//    private String getColumnMethodName(Class<?> fieldType) {
//        String typeName;
//        if (fieldType.isPrimitive()) {
//            typeName = DaoUtil.capitalize(fieldType.getName());
//        } else {
//            typeName = fieldType.getSimpleName();
//        }
//        String methodName = "get" + typeName;
//        if ("getBoolean".equals(methodName)) {
//            methodName = "getInt";
//        } else if ("getChar".equals(methodName) || "getCharacter".equals(methodName)) {
//            methodName = "getString";
//        } else if ("getDate".equals(methodName)) {
//            methodName = "getLong";
//        } else if ("getInteger".equals(methodName)) {
//            methodName = "getInt";
//        }
//        return methodName;
//    }

    // 通过反射，obj--contentValues
    /**
     * ContentValues values = new ContentValues();
     * values.put("name", person.getName());
     * db.insert("Person", null, values);
     */
    private ContentValues contentValuesByObj(T obj) {
        ContentValues values = new ContentValues();
        // values.put(String ,Long);
        Field[] fields = mClazz.getDeclaredFields(); // 反射：拿到类的所有属性
        for (Field field : fields) { // 遍历所有属性
            field.setAccessible(true); // 设置权限，私有和公有都可访问。
            try {
                String key = field.getName(); // 属性的名称，对应表的字段
                Object value = field.get(obj); // 获取对象obj，里面属性field的值【field对应的】。
                // 使用反射, 获取put方法，然后通过反射执行。
                // put的第二个参数，是带类型的。putInt,putString,
                mPutMethodArgs[0] = key;
                mPutMethodArgs[1] = value;
                // 缓存put方法: putInt, putString, putBoolean
                // fieldTypeName = value.getClass().getName();
                String fieldTypeName = field.getType().getName();
                Method putMethod = mPutMethods.get(fieldTypeName); // 先从缓存获取方法。
                if (putMethod == null) {
                    // values.put(key, 11);
                    // ContentValues的put方法，反射获取put方法，
                    putMethod = ContentValues.class.getDeclaredMethod("put", String.class, value.getClass());
                    mPutMethods.put(fieldTypeName, putMethod); // 添加进缓存
                }
                // 通过反射 执行方法
//                putMethod.invoke(values, key, value); // 三个参数的invoke方法
                putMethod.invoke(values, mPutMethodArgs); // 替代方法，执行values.put("name", person.getName());
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                mPutMethodArgs[0] = null;
                mPutMethodArgs[1] = null;
            }
        }
        return values;
    }

}

